#ifndef _MAT_1D_H_
#define _MAT_1D_H_

#include "./math_mat_def.h"

namespace Mathematics {
	namespace Matrix {
		
		//! Class for manipulating a set of ordered double numbers.
		/*!
			A simple Matrix with just one dimension.
			Used in the higher dimensions Matrices.
			Especially defined for freely manipulate values, sets and sub-sets.
		*/
		class Matrix1D: public Wtb {
		private:
			
				// -attributes-
			/*! Integer cointaing the unique identifier in the class. */
			uint id;
			/*! Flag indicating if this is an alias for memory region. */
			bool copied;
			
			/*! Matrix dimension. */
			uint dim;
			/*! Pointer for the matrix values. */
			double *value;
			
				// -static attributes-
			/*! Integer cointaing the next available identifier for class objects. */
			static uint next_id;
			
				// -static private methods-
			static inline uint assignId(){ return next_id++; };
			static inline uint restartId(){ return next_id = 0; };
			
				// -private methods-
			void copy(const Matrix1D &original);
			bool create();
			void free();
			
			Matrix1D();
			
		public:
			
				// -constructors-
			/*! 
				Constructor
				
				\param dimension The quantity of values in the Matrix.
				\code
				Matrix1D mat(5);	// creates a Matrix with 5 positions from 0 to 4.
				\endcode
			*/
			Matrix1D(const uint &dimension);
			/*! 
				Copy Constructor
				
				\param original The object to be copied.
				\sa Matrix1D::Matrix1D(const uint &dimension)
				
				\code
				Matrix1D mat(5);	// creates a Matrix with 5 positions from 0 to 4.
				// ...
				Matrix1D copy( mat );	// creates a new Matrix with the same dimension and values.
				\endcode
			*/
			Matrix1D(const Matrix1D &original);
			/*! 
				Destructor
			*/
			~Matrix1D();
			
				// -getters & setters-
			//! getter for the id
			inline uint getID() const { return id; };
			//! getter for the copy flag
			inline uint getCopied() const { return copied; };
			//! getter for the dimension value
			inline uint getDim() const { return dim; };
			/*! 
				Method for getting values in the matrix.
				Can throw OutOfBoundsException if the parameter is invalid.
				
				\param index Index of the desired value.
				\return copy of the desired value.
				\sa void Matrix1D::setValue(const uint &index, const double &item)
				\sa double& operator[](const uint &index)const
				
				\code
				Matrix1D mat(5);
				mat.setValue(0, 5.0);
				cout << mat.getValue(0) << endl; // prints 5.0
				\endcode
			*/
			inline double getValue(const uint &index) const {
				if( index >= dim ){
					throw OutOfBoundsException("Acesso Invalido.",dim,index);
				}
				return value[index];
			};
			/*! 
				Method for changing values in the matrix
				
				\param index Index of the desired position.
				\param item The desired new value.
				\sa double Matrix1D::getValue(const uint &index)const
				\sa double& Matrix1D::operator[](const uint &index)const
				
				\code
				Matrix1D mat(5);
				mat.setValue(0, 5.0);
				cout << mat.getValue(0) << endl; // prints 5.0
				\endcode
			*/
			inline void setValue(const uint &index, const double &item){
				if(index >= dim){
					throw OutOfBoundsException("Acesso Invalido.",dim,index);
				}
				value[index] = item;
			};
			/*! 
				Method for getting chunks of the matrix
				
				\param index Index of the first value.
				\param size Size of the desired Matrix piece.
				\return copy of the desired area in a new Matrix
				\sa Matrix1D::operator()(const uint &begin_index, const uint &end_index)
				
				\code
				Matrix1D mat(5);
				mat, 1, 2, 3, 4, 5;
				Matrix1D part(3);
				part = mat.getSelection(2,3); // part has now the value [ 3 4 5 ]
				\endcode
			*/
			Matrix1D getSelection(const uint &index, const uint &size) const;
			/*! 
				Method for getting chunks of the Matrix.
				The size is the rest of the Matrix' dimension
				
				\param index Index of the first value.
				\return copy of the desired area in a new Matrix
				
				\sa Matrix1D::getSelection(const uint &index, const uint &size)const
			*/
			inline Matrix1D getSelection(const uint &index) const {
				return getSelection(index,dim - index);
			};
			/*! 
				Method for setting chunks of the matrix
				Can throw OutOfBoundsException if exceds the original matrix
				
				\param index Index of the first value.
				\param part Matrix piece desired to place.
				\sa Matrix1D::operator()(const uint &begin_index, const uint &end_index)
				
				\code
				Matrix1D part(2);
				part, 6, 7;
				Matrix1D mat(5);
				mat, 1, 2, 3, 4, 5;
				mat.setSelection(2,part); // mat has now the value [ 1 2 6 7 5 ]
				\endcode
			*/
			void setSelection(const uint &index, const Matrix1D &part);
			/*! 
				Method for setting chunks of the matrix in the beginning (index = 0)
				Can throw OutOfBoundsException if exceds the original matrix
				
				\param part Matrix piece desired to place.
				\sa Matrix1D::setSelection(const uint &index, const Matrix1D &part)
			*/
			void setSelection(const Matrix1D &part);
			
				// -operators-
			/*! 
				Matrix attribution
				
				\param mat Matrix desired to attribution.
				
				\code
				Matrix1D copy(3), alias(3), mat(3);
				mat, 1, 2, 3;
				alias = copy = mat; // alias and copy have now the value [ 1 2 3 ]
				\endcode
			*/
			Matrix1D& operator = (const Matrix1D &mat);
			/*! 
				Matrix sum
				
				\param mat Matrix desired to sum.
				\sa Matrix1D::operator+=(const Matrix1D &mat)
				
				\code
				Matrix1D mat(3);
				mat, 1, 2, 3;
				cout << mat + mat << endl; // prints [ 2 4 6 ]
				\endcode
			*/
			Matrix1D operator + (const Matrix1D &mat) const;
			/*! 
				Matrix sum with left attribution
				
				\param mat Matrix desired to sum.
				\sa Matrix1D::operator=(const Matrix1D &mat)
				\sa Matrix1D::operator+(const Matrix1D &mat)const
				
				\code
				Matrix1D atr(3), mat(3);
				atr, 1, 2, 3;
				mat, 5, 6, 7;
				atr += mat; // atr has now the value [ 5 6 7 ]
				\endcode
			*/
			Matrix1D& operator += (const Matrix1D &mat);
			/*! 
				Matrix subtraction
				
				\param mat Matrix desired to subtract.
				\sa Matrix1D::operator-=(const Matrix1D &mat)
				
				\code
				Matrix1D mat(3);
				mat, 1, 2, 3;
				cout << mat - mat << endl; // prints [ 0 0 0 ], pretty dumb!
				\endcode
			*/
			Matrix1D operator - (const Matrix1D &mat) const;
			/*! 
				Matrix subtraction with left attribution
				
				\param mat Matrix desired to subtract.
				\sa Matrix1D::operator=(const Matrix1D &mat)
				\sa Matrix1D::operator-(const Matrix1D &mat)const
				
				\code
				Matrix1D atr(3), mat(3);
				atr, 1, 2, 3;
				mat, 5, 6, 7;
				atr -= mat; // atr has now the value [ -4 -4 -4 ]
				\endcode
			*/
			Matrix1D& operator -= (const Matrix1D &mat);
			/*! 
				Matrix multiplication by number
				
				\param scalar number desired to multiply the Matrix.
				\sa Matrix1D::operator*=(const double &scalar)
				
				\code
				Matrix1D mat(3);
				mat, 1, 2, 3;
				cout << mat * 5 << endl; // prints [ 5 10 15 ]
				\endcode
			*/
			Matrix1D operator * (const double &scalar) const;
			/*! 
				Matrix multiplication by number - comutative
				
				\param scalar number desired to multiply the Matrix.
				\param mat the Matrix.
				\sa Matrix1D::operator*(const double &scalar)
				
				\code
				Matrix1D mat(3);
				mat, 1, 2, 3;
				cout << 5 * mat << endl; // prints [ 5 10 15 ]
				\endcode
			*/
			friend inline Matrix1D operator * (const double &scalar, const Matrix1D &mat){
				return mat*scalar;
			};
			/*! 
				Matrix multiplication by number with left attribution
				
				\param scalar number desired to multiply the Matrix.
				\sa Matrix1D::operator=(const Matrix1D &mat)
				\sa Matrix1D::operator*(const double &scalar)const
				
				\code
				Matrix1D mat(3);
				mat, 1, 2, 3;
				atr *= 3; // mat has now the value [ 3 6 9 ]
				\endcode
			*/
			Matrix1D& operator *= (const double &scalar);
			/*! 
				Matrix division by number
				Can throw DivisionByZeroException.
				
				\param scalar number desired to divide the Matrix. If zero Throws DivisionByZeroException.
				\sa Matrix1D::operator/=(const double &scalar)
				\sa class DivisionByZeroException
				
				\code
				Matrix1D mat(3);
				mat, 1, 2, 3;
				cout << mat / 5 << endl; // prints [ 0.2 0.4 0.6 ]
				\endcode
			*/
			Matrix1D operator / (const double &scalar) const;
			/*! 
				Matrix division by number with left attribution.
				Can throw DivisionByZeroException.
				
				\param scalar number desired to divide the Matrix. If zero Throws DivisionByZeroException.
				\sa Matrix1D::operator=(const Matrix1D &mat)
				\sa Matrix1D::operator/(const double &scalar)const
				\sa class DivisionByZeroException
				
				\code
				Matrix1D mat(3);
				mat, 1.5, 3.0, 9.3;
				atr /= 3.0; // mat has now the value [ 0.5 1.0 3.1 ]
				\endcode
			*/
			Matrix1D& operator /= (const double &scalar);
			/*! 
				Operator for manipulating chunks of a Matrix
				
				\param begin_index Index of the first value.
				\param end_index Index of the final value (including).
				\return a reference to the desired area. It can work as a Matrix too.
				\sa Matrix1D::getSelection(const uint &index, const uint &size)const
				
				\code
				Matrix1D mat(5);
				mat, 1, 2, 3, 4, 5;
				mat(2,4), 6, pr, 8;
				cout << mat(1,4) << endl; // prints [ 2 6 4 8 ]
				cout << mat(1,4)(0,1) << endl; // prints [ 2 6 ]
				\endcode
			*/
			Matrix1D& operator () (const uint &begin_index, const uint &end_index) ;
			/*! 
				Operator for inserting values in order in the Matrix
				
				\param num value to be inserted in the first index of the Matrix.
				\return a reference to the rest. It can work as a Matrix too. You can keep using this operator.
				\sa Matrix1D::operator()(const uint &begin_index, const uint &end_index)
				
				\code
				Matrix1D mat(5);
				mat, 1, 2, 3, 4, 5;
				cout << mat << endl; // prints [ 1 2 3 4 5 ]
				cout << ( mat, 8, 9 ) << endl; // prints [ 3 4 5 ] and mat has value [ 8 9 3 4 5 ]
				\endcode
			*/
			Matrix1D& operator , (const double &num);
			/*! 
				Operator for doing actions in insertion order in the Matrix
				
				\param action some action to be made.
				\return a reference to the rest. It can work as a Matrix too. You can keep using this operator.
				\sa Matrix1D::operator,(const double &num)
				
				\code
				Matrix1D mat(5);
				mat, 1, 2, 3, 4, 5;
				mat, pr, pr, 6, 7;		// mat has now value [ 1 2 6 7 5 ]
				\endcode
			*/
			Matrix1D& operator , (const MatrixManipulation &action);
			/*! 
				Operator for accessing individual elements in the Matrix
				
				\param index position to be accessed int the Matrix.
				\return the value in that position.
				
				\code
				Matrix1D mat(5);
				mat, 1, 2, 3, 4, 5;
				cout << mat[1] << endl;		// prints 2
				\endcode
			*/
			double& operator [] (const uint &index) const ;
			
				// -internal public methods-
			
				// -read & write-
			/*! 
				Method for converting the Matrix in a string representation
				
				\return a string that represents the Matrix.
				
				\code
				Matrix1D mat(5);
				mat, 1, 2, 3, 4, 5;
				cout << mat.toS() << endl;		// prints [ 1 2 3 4 5 ]
				\endcode
			*/
			virtual string toS() const ;
			/*! 
				Writes the value of the Matrix in an opened file.
				
				\param parq Pointer to the opened file.
				\return boolean indicating failure or success of the operation
				
				\sa string Matrix1D::toS()const
				
				\code
				Matrix1D mat(5);
				mat, 1, 2, 3, 4, 5;
				mat.filePrint();		// prints [ 1 2 3 4 5 ] on the terminal
				\endcode
			*/
			virtual bool filePrint( FILE *parq = stdin ) const ;
			
			/*! 
			*/
			double* toPointer() const ;
				// -image-
			
				// -test-
			/*! 
				Method for Debug
				
				\code
				Matrix1D::test();
				\endcode
			*/
			static void test();
			
		};
		
			// -external operators-
		
			// -external methods-
		
	};
};

#endif
